import random
import string

def attributeStep(attribute):
    "Return attribute's step"
    #odd little attribute to step function - at least it works to a 100 following the books pattern
    return int(round(float(attribute)/3.0 + .2, 0) + 1.0)
 
def stepDice(step):
    "Return dice from step"
    dice = ('d4-2', \
          'd4-1', \
          'd4', \
          'd6', \
          'd8', \
          'd10', \
          'd12', \
          '2d6', \
          'd8+d6', \
          'd10+d6', \
          'd10+d8', \
          '2d10', \
          'd12+d10', \
          'd20+d4', \
          'd20+d6', \
          'd20+d8', \
          'd20+d10', \
          'd20+d12', \
          'd20+2d6', \
          'd20+d8+d6', \
          'd20+d10+d6', \
          'd20+d10+d8', \
          'd20+2d10', \
          'd20+d12+d10', \
          'd20+d10+d8+d4', \
          'd20+d10+d8+d6', \
          'd20+d10+2d8', \
          'd20+2d10+d8', \
          'd20+d12+d10+d8', \
          'd20+d10+d8+2d6', \
          'd20+d10+2d8+d6', \
          'd20+2d10+d8+d6', \
          'd20+2d10+2d8', \
          'd20+3d10+d8', \
          'd20+d12+2d10+d8', \
          '2d20+d10+d8+d4', \
          '2d20+d10+d8+d6', \
          '2d20+d10+2d8', \
          '2d20+2d10+d8', \
          '2d20+d12+d10+d8', \
          '2d20+d10+d8+2d6', \
          '2d20+d10+2d8+d6', \
          '2d20+2d10+d8+d6', \
          '2d20+2d10+2d8', \
          '2d20+3d10+d8', \
          '2d20+d12+2d10+d8', \
          '2d20+2d10+2d8+d4', \
          '2d20+2d10+2d8+d6', \
          '2d20+2d10+3d8', \
          '2d20+3d10+2d8', \
          '2d20+d12+2d10+2d8', \
          '2d20+2d10+2d8+2d6', \
          '2d20+2d10+3d8+d6', \
          '2d20+3d10+2d8+d6', \
          '2d20+3d10+3d8', \
          '2d20+4d10+2d8', \
          '2d20+d12+3d10+2d8', \
          '3d20+2d10+2d8+d4', \
          '3d20+2d10+2d8+d6', \
          '3d20+2d10+3d8', \
          '3d20+3d10+2d8', \
          '3d20+d12+2d10+2d8', \
          '3d20+2d10+2d8+2d6', \
          '3d20+2d10+3d8+d6', \
          '3d20+3d10+2d8+d6', \
          '3d20+3d10+3d8', \
          '3d20+4d10+2d8', \
          '3d20+d12+3d10+2d8', \
          '3d20+3d10+3d8+d4', \
          '3d20+3d10+3d8+d6', \
          '3d20+3d10+4d8', \
          '3d20+4d10+3d8', \
          '3d20+d12+3d10+3d8', \
          '3d20+3d10+3d8+2d6', \
          '3d20+3d10+4d8+d6', \
          '3d20+4d10+3d8+d6', \
          '3d20+4d10+4d8', \
          '3d20+5d10+3d8', \
          '3d20+d12+4d10+3d8', \
          '4d20+3d10+3d8+d4', \
          '4d20+3d10+3d8+d6', \
          '4d20+3d10+4d8', \
          '4d20+4d10+3d8', \
          '4d20+d12+3d10+3d8', \
          '4d20+3d10+3d8+2d6', \
          '4d20+3d10+4d8+d6', \
          '4d20+4d10+3d8+d6', \
          '4d20+4d10+4d8', \
          '4d20+5d10+3d8', \
          '4d20+d12+4d10+3d8', \
          '4d20+4d10+4d8+d4', \
          '4d20+4d10+4d8+d6', \
          '4d20+4d10+5d8', \
          '4d20+5d10+4d8', \
          '4d20+d12+4d10+4d8', \
          '4d20+4d10+4d8+2d6', \
          '4d20+4d10+5d8+d6', \
          '4d20+5d10+4d8+d6', \
          '4d20+5d10+5d8', \
          '4d20+6d10+4d8')
    return dice[step-1]

def stepRoller(step):
    "Return a step's dice roll"
    # Given a step number between 1 and 100, it will return the total as an int
    # Given a step number less than 1 or greater than 100, return 0
    if (step <1) or (step > 100) :
        calculation = 0
    else:
        random.seed() #you can take this out if you're seeding the RNG somewhere else
        dice = stepDice (step).split("+")
        calculation = 0
        for roll in dice:
            calculation += dieRoll (roll)
    return calculation

def dieRoll(dice):
    "Returns the roll of one die, can roll multiple times on max."
    "Always returns at least 1 for valid input."
    "Returns 0 on invalid input. Or returns something weird."
    "Should never error out on invalid input anyway."
    minusparts = dice.split("-")
    nums = minusparts[0].split("d")
    if len(nums) == 1: # no "d" in operation, invalid input
        returnvalue = 0
    else:
        if nums[0] == "":
            quantity = 1
        else:
            quantity=toInteger(nums[0])
        sides=toInteger(nums[1])
        returnvalue=0
        if sides>0:
            if len(minusparts)>1:
                minusamount = toInteger(minusparts[1])
            else:
                minusamount = 0
                #minusamount will be subtracted on every roll
                while quantity>0:
                    roll = sides #roll at least once
                    while roll == sides: # Keep rolling until we get less than the max
                        roll = random.randint(1,sides)
                        addamount = roll - minusamount
    
                        if addamount<=1:
                            addamount = 1
                        returnvalue += addamount
                        quantity -= 1
        else:
            returnvalue = 0 #invalid sides part
        return returnvalue


def toInteger(testvalue):
    "Returns the integer value of a string or returns 0 if no conversion possible"
    if len(testvalue)== 0:
        return 0
    else:
        for c in list(str(testvalue)):
            if not c in string.digits:
                return 0
            else:
                return int(testvalue)
            
def testResult (difficulty, roll):
    #takes a roll and a difficult and returns the success level as a string
    #this is an expensive function -- need to ensure it gets generated the minimum amount of times
    #also need to incorportate 'rule of one'
    
    #first we set up success table (how fun!)
    table = {2 : [[0], 1, range(2,5), range(5,7), range(7,9)]}
    table[3] = [[0], range(1,3), range(3,5), range(6,8), range(8,10)]
    table[4] = [[0], range(1,4), range(4,7), range(7,10), range(10,12)]
    table[5] = [[0], range(1,5), range(5,9), range(9,11), range(11,14)]
    table[6] = [[1], range(2,6), range(6,10), range(10,13), range(13,17)]
    table[7] = [[1], range(2,7), range(7,12), range(12,15), range(15,19)]
    table[8] = [range(1,3), range(3,8), range(8,13), range(13,16), range(16,20)]
    table[9] = [range(1,4), range(4,9), range(9,14), range(14,18), range(18,22)]
    table[10] = [range(1,5), range(5,10), range(10,15), range(15,19), range(19,24)]
    table[11] = [range(1,5), range(5,11), range(11,17), range(17,21), range(21,25)]
    table[12] = [range(1,6), range(6,12), range(12,18), range(18,22), range(22,27)]
    table[13] = [range(1,6), range(6,13), range(13,20), range(20,24), range(24,29)]
    table[14] = [range(1,7), range(7,14), range(14,21), range(21,26), range(26,32)]
    table[15] = [range(1,8), range(8,15), range(15,22), range(22,27), range(27,33)]
    table[16] = [range(1,8), range(8,16), range(16,24), range(24,29), range(29,35)]
    table[17] = [range(1,9), range(9,17), range(17,25), range(25,30), range(30,37)]
    table[18] = [range(1,10), range(10,18), range(18,26), range(26,32), range(32,38)]
    table[19] = [range(1,11), range(11,19), range(19,27), range(27,33), range(33,39)]
    table[20] = [range(1,12), range(12,20), range(20,28), range(28,34), range(34,41)]
    table[21] = [range(1,13), range(13,24), range(21,29), range(29,35), range(35,42)]
    table[22] = [range(1,13), range(13,25), range(22,31), range(31,37), range(37,44)]
    table[23] = [range(1,14), range(14,26), range(23,32), range(32,38), range(38,45)]
    table[24] = [range(1,15), range(15,24), range(24,33), range(33,40), range(40,47)]
    table[25] = [range(1,16), range(16,25), range(25,34), range(34,41), range(41,48)]
    table[26] = [range(1,17), range(17,26), range(26,35), range(35,42), range(42,49)]
    table[27] = [range(1,18), range(18,27), range(27,36), range(36,44), range(44,51)]
    table[28] = [range(1,18), range(18,28), range(28,38), range(38,45), range(45,55)]
    table[29] = [range(1,19), range(19,29), range(29,39), range(39,46), range(46,54)]
    table[30] = [range(1,20), range(20,30), range(30,40), range(40,47), range(47,55)]
    table[31] = [range(1,21), range(21,31), range(31,41), range(41,48), range(48,56)]
    table[32] = [range(1,22), range(22,32), range(32,42), range(42,49), range(49,58)]
    table[33] = [range(1,23), range(23,33), range(33,43), range(43,51), range(51,59)]
    table[34] = [range(1,23), range(23,34), range(34,45), range(45,55), range(52,61)]
    table[35] = [range(1,24), range(24,35), range(35,46), range(46,53), range(53,65)]
    table[36] = [range(1,25), range(25,36), range(36,47), range(47,55), range(55,64)]
    table[37] = [range(1,26), range(26,37), range(37,48), range(48,56), range(56,65)]
    table[38] = [range(1,26), range(26,38), range(38,50), range(50,58), range(58,67)]
    table[39] = [range(1,27), range(27,39), range(39,51), range(51,59), range(59,68)]
    table[40] = [range(1,28), range(28,40), range(40,52), range(52,60), range(60,70)]
    
    #set up text conversion and default at extraordinary (safe?)
    successResult = {0: 'pathetic', 1: 'poor', 2: 'average', 3: 'good', 4: 'excellent', 5: 'extraordinary'}
    success = 5
    
    for index, item in enumerate(table[difficulty]):
        if roll in item:
            success = index
    
    return successResult[success]